package jp.wug.samples.hangman;

import com.google.inject.Inject;
import jp.wug.ConversationScoped;
import jp.wug.Renderer;

import java.util.*;

public class Hangman {
    private static char[] CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray();
    private static int MAX_GUESSES = 5;

    private final Game game;

    @Inject
    Hangman(Game game) {
        this.game = game;
    }

    @ConversationScoped
    static class Game {
        private static final List<String> WORDS = splitWords("java,source,template,logic,design");
        private static final Random random = new Random();

        private static List<String> splitWords(String src) {
            List<String> list = new ArrayList<String>();
            for (String s : src.split(",")) {
                list.add(s.trim().toUpperCase());
            }
            return Collections.unmodifiableList(list);
        }

        private String word;
        private final Set<Character> guessedCharacters = new HashSet<Character>();

        Game() {
            next();
        }

        void next() {
            word = WORDS.get(random.nextInt(WORDS.size()));
            guessedCharacters.clear();
        }

        boolean isGuessed(char c) {
            return guessedCharacters.contains(c);
        }

        String getWord() {
            return word;
        }

        void guess(char c) {
            guessedCharacters.add(c);
        }

        int getIncorrectCount() {
            int n = 0;
            for (char c : guessedCharacters) {
                if (word.indexOf(c) == -1) {
                    n++;
                }
            }
            return n;
        }
    }

    private Vocab getVocab(char c) {
        return new Vocab(game, c);
    }

    public static class Vocab {
        private final Game game;
        private final char c;
        private final boolean isGuessed;

        Vocab(Game game, char c) {
            this.game = game;
            this.c = c;
            this.isGuessed = game.isGuessed(c);
        }

        public void guess() {
            game.guess(c);
        }

        public boolean isGuessed() {
            return isGuessed;
        }

        public String getAltValue() {
            return Character.toString(c);
        }

        public String getImageURL() {
            return "images/Chalkboard_" + c + ".png";
        }
    }

    public int getGuessesLeft() {
        return MAX_GUESSES - game.getIncorrectCount();
    }

    public boolean hasGuessesLeft() {
        return getGuessesLeft() >= 0;
    }

    public String guessesLeftAsImage() {
        return "images/Chalkboard_" + getGuessesLeft() + ".png";
    }

    public String getScaffoldAsString() {
        String message = "HANGMAN";
        int n = message.length() - 1 - getGuessesLeft();
        return n < 0 ? "" : message.substring(0, n);
    }

    public String getScaffoldAsImage() {
        return "images/scaffold_" + getGuessesLeft() + ".png";
    }

    public void renderVocab(Renderer<Integer, Vocab> renderer) {
        String s = game.getWord();
        for (int i = 0; i < s.length(); i++) {
            renderer.render(i, getVocab(s.charAt(i)));
        }
    }

    public Vocab getVocabAt(Integer key) {
        char c = game.getWord().charAt(key);
        return getVocab(c);
    }

    public void renderAvailableCharacters(Renderer<String, Vocab> renderer) {
        for (char c : Hangman.CHARACTERS) {
            if (!game.isGuessed(c)) {
                renderer.render(Character.toString(c), getVocab(c));
            }
        }
    }

    public Vocab getVocab(String key) {
        char c = key.charAt(0);
        return getVocab(c);
    }

    public boolean isWin() {
        for (char c : game.getWord().toCharArray()) {
            if (!game.isGuessed(c)) {
                return false;
            }
        }
        return true;
    }

    public boolean isGameEnded() {
        return !hasGuessesLeft() || isWin();
    }

    public void startNewGame() {
        game.next();
    }
}